package com.xiaoxi.init.utils;

import com.xiaoxi.init.common.ErrorCode;
import com.xiaoxi.init.exception.BusinessException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Slf4j
public class JwtUtils {

    //加密算法
    private final static SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;

    //私钥 / 生成签名的时候使用的秘钥secret，一般可以从本地配置文件中读取，切记这个秘钥不能外露，只在服务端使用，在任何场景都不应该流露出去。
    // 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
    private final static String secret = "secretKey";

    // 过期时间（单位秒）/ 2小时
    private final static Long access_token_expiration = 2 * 60 * 60L;

    //jwt签发者
    private final static String jwt_iss = "xiaoxi";

    //jwt所有人
    private final static String subject = "xiaoxiya";

    /**
     * 创建jwt
     *
     * @return 返回生成的jwt token
     */
    public static String generateJwtToken(Long userId) {

        // 头部 map / Jwt的头部承载，第一部分
        // 可不设置 默认格式是{"alg":"HS256"}
        Map<String, Object> map = new HashMap<>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");


        //载荷 map / Jwt的载荷，第二部分
        Map<String, Object> claims = new HashMap<String, Object>();

        //私有声明 / 自定义数据，根据业务需要添加
        claims.put("userId", Long.toString(userId));

        //标准中注册的声明 (建议但不强制使用)
        //一旦写标准声明赋值之后，就会覆盖了那些标准的声明
        claims.put("iss", jwt_iss);
            /*	iss: jwt签发者
                sub: jwt所面向的用户
                aud: 接收jwt的一方
                exp: jwt的过期时间，这个过期时间必须要大于签发时间
                nbf: 定义在什么时间之前，该jwt都是不可用的.
                iat: jwt的签发时间
                jti: jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击
            */


        //下面就是在为payload添加各种标准声明和私有声明了
        return Jwts.builder() // 这里其实就是new一个JwtBuilder，设置jwt的body
                .setHeader(map)         // 头部信息
                .setClaims(claims)      // 载荷信息
                .setId(UUID.randomUUID().toString()) // 设置jti(JWT ID)：是JWT的唯一标识，从而回避重放攻击。
                .setIssuedAt(new Date())       // 设置iat: jwt的签发时间
                .setExpiration(new Date(System.currentTimeMillis() + access_token_expiration * 1000)) // 设置exp：jwt过期时间
                .setSubject(subject)    //设置sub：代表这个jwt所面向的用户，所有人
                .signWith(SIGNATURE_ALGORITHM, secret)//设置签名：通过签名算法和秘钥生成签名
                .compact(); // 开始压缩为xxxxx.yyyyy.zzzzz 格式的jwt token
    }

    /**
     * 从token中获取 载荷 信息
     *
     * @param token
     * @return
     */
    public static Claims getClaimsFromJwt(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return claims;
    }

    /**
     *  解析token 获取登录用户信息
     * @param toekn
     * @return
     */
    public static Long getLoginUserVOFromJwt(String toekn) {
        Claims claims = null;
        try {
            // 解析JWT
            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(toekn).getBody();
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        // 判断token是否过期
        if (claims.getExpiration().before(new Date())){
            throw new BusinessException(ErrorCode.AUTH_EXPIRED_ERROR);
        }
        String userIdStr = claims.get("userId").toString();
        if (userIdStr == null){
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        return Long.parseLong(userIdStr);
    }

    /**
     * 验证JWT是否过期
     *
     * @param jwt JWT字符串
     * @return 如果JWT已过期则返回true，否则返回false
     */
    public static boolean isJwtExpired(String jwt) {
        try {
            // 使用相同的密钥解析JWT
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(jwt).getBody();

            // 获取JWT的过期时间声明
            Date expiration = claims.getExpiration();

            // 检查当前时间是否已超过过期时间
            return expiration.before(new Date());
        } catch (ExpiredJwtException e) {
            // 如果直接捕获到ExpiredJwtException，说明JWT已过期
            return true;
        } catch (Exception e) {
            // 其他异常情况，可能JWT格式错误等，这里简单处理为未过期，具体可以根据需求调整
            e.printStackTrace();
            return false;
        }
    }
}
